import axios, {AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse} from "axios";
import {NetSetting} from "@/setting";
import {Message} from "@/utils/Message";
import {BusinessCode} from "@/lang/Common";
import {JwtUtils} from "@/utils/Jwt";
import {Token} from "@/utils/Token";
import {refreshTokenService} from "@/service/auth";
import {BaseResponse} from "@/entity/net";
import AppRouter from "@/router";

const Block_WiteList = ["/authen/refreshToken", "/authen/logout"];
/**
 * 创建Service实例
 */
export const BaseService: AxiosInstance = axios.create({
    baseURL: NetSetting.baseUrl
});

// 阻塞当前请求，将其放入队列中，等待回调被调用时再次发起请求
function retryRequest(config: AxiosRequestConfig): Promise<AxiosRequestConfig> {
    return new Promise<AxiosRequestConfig>(resolve => {
        // console.log("将当前请求放入队列中");
        // 将请求放入订阅回调队列中
        Token.subscribe(() => {
            // 这里要重新获取一下token
            // console.log(config);
            // console.log("准备开始重新请求");
            const accessToken = JwtUtils.getAccessToken();
            if (accessToken != null) {
                config.headers![NetSetting.authorization] = JwtUtils.createAuthorizationHeader(NetSetting.jwtType, accessToken);
            }
            // 再次发起请求
            resolve(config);
        });
    });
}

// 将触发Token刷新的请求重试
function retryErrResponse(error: AxiosError<BaseResponse<never>>): any {
    return new Promise(resolve => {
        Token.subscribe(() => {
            const accessToken = JwtUtils.getAccessToken();
            // 获取上一个请求的配置
            const prevRequestConfig = error.config as AxiosRequestConfig;
            // 需要将header重新解析赋值一下变成一个普通对象，否则会报错：is not a valid HTTP header field value
            prevRequestConfig.headers = {...prevRequestConfig.headers};
            // 重新获取一下token
            if (accessToken != null) {
                prevRequestConfig.headers![NetSetting.authorization] = JwtUtils.createAuthorizationHeader(NetSetting.jwtType, accessToken);
            }
            resolve(BaseService.request(prevRequestConfig));
        });
    });
}

// 准备进行刷新token
function readyToRefresh(error: AxiosError<BaseResponse<never>>): any {
    if (!Token.isBlock()) {
        // console.log("当前未阻塞");
        Token.block();
        // console.log("请求已阻塞");
        refreshTokenService().then(res => {
            // console.log("刷新结果", res);
            return;
        }).finally(() => {
            // console.log("开始释放阻塞队列的请求");
            Token.release();
            Token.notify();
        });
        // console.log("将触发Token刷新的请求放入到队列中");
        // 将触发token刷新的请求也放入到请求队列中
        return retryErrResponse(error);
    }
    return error;
}

/**
 * Axios请求拦截
 */
BaseService.interceptors.request.use((config: AxiosRequestConfig): Promise<AxiosRequestConfig> => {
    // 获取token
    const accessToken = JwtUtils.getAccessToken();
    // console.log(config.url);
    // console.log(Block_WiteList.includes(config.url as string));
    // console.log(Token.isBlock());
    // 判断是否阻塞所有非白名单的请求
    if (!Block_WiteList.includes(config.url as string) && Token.isBlock()) {
        return retryRequest(config);
    } else {
        // 如果token存在的话
        if (accessToken !== null) {
            // 在每一次请求的时候都带上token
            config.headers![NetSetting.authorization] = JwtUtils.createAuthorizationHeader(NetSetting.jwtType, accessToken);
        }
        return Promise.resolve(config);
    }
});

/**
 * Axios响应拦截
 */
BaseService.interceptors.response.use((response: AxiosResponse): AxiosResponse => {
    // 输出响应体内容
    console.log(response);
    // 对业务码进行相应的判断，主要是对几种特殊情况做处理
    switch (response.data?.code) {
        case BusinessCode.SuccessLogin:
        case BusinessCode.SuccessRefresh: {
            if (response.data?.data.access) {
                JwtUtils.setAccessToken(response.data?.data.access);
            }
            if (response.data?.data.refresh) {
                JwtUtils.setRefreshToken(response.data?.data.refresh);
            }
            break;
        }
    }
    return response;
}, (error: AxiosError<BaseResponse<never>>) => {
    console.log(error);
    const responseData = error.response?.data;

    switch (responseData?.code) {
        case BusinessCode.RefreshToken: {
            // console.log("后台返回401状态码，准备进行token刷新请求");
            return readyToRefresh(error);
        }
        case BusinessCode.ExpiredToken: // token完全过期
        case BusinessCode.TokenInvalid: // 非法的token
        case BusinessCode.AccessNoLogin: { // 没有登录
            // 清空本地的认证数据
            JwtUtils.clearAuth();
            // 跳转到登录界面
            AppRouter.replace({name: "login"});
            break;
        }
    }
    Message.error(responseData!.msg);
    return error;
});